<?PHP
/**
 * OpenStation
 *
 * @Application : Cache.class.php
 * @Encode : 65001 Unicode ( UTF-8 NO BOM )
 * @Description : 变量缓存及操作集
 * @Author : HK <hkyyang@gmail.com>
 * @Version : 1.91
 */

class Cache {

    // 静态变量缓存
    static private $_cache = array();
    // 押入数组末尾(入栈)通配符
    static private $_auto = '*';
    // “.”替换符
    static private $_dot = '#';

    const LANG_NS = 'CACHE_LANGUAGE_NAMESPACE';

    // 赋值运算模板
    static private $operators = array(
        // 数字赋值运算
        '+=' => '+= $value',
        '-=' => '-= $value',
        '=-' => '= $value - $last_value',
        '*=' => '*= $value',
        '/=' => '= $last_value == 0 ? 0 : ( $value != 0 ? $last_value / $value : $last_value )',
        '=/' => '= $value == 0 ? 0 : ( $last_value != 0 ? $value / $last_value : $value )',
        '%=' => '= $value == 0 ? 0 : $last_value % $value',
        // 字符串赋值运算
        '.=' => '.= $value', // 追加
        '=.' => '= $value . $last_value', // 前置
    );


    /**
     * set 方法，设置变量缓存
     * Array 不被处理为普通非标量，而是算做多维赋值，并且数组内键名大写化
     * 键名必须以"." 构造，支持无限级构造
     * 可以存储任何标量及非标量
     * 运算符只在值为标量( 除Boolean )并且有原值时有效, 否则会产生不可预料的结果
     *
     * @static
     * @access public
     * @param String | Array : $name, 变量名称、键名构造或配置数组
     * @param * : $value, 变量值
     * @param String | Null : $operator, 运算符, 必须为赋值运算
     * @param Boolean : $upperCase, 索引大写化
     * @return *
     */
    static public function set( $name, $value = NULL, $operator = NULL, $upperCase = TRUE  ) {
        // 非标量和布尔键名过滤
        if( empty( $name ) || ( ! is_scalar( $name ) && ! is_array( $name ) ) || is_bool( $name ) ) {
            return NULL;
        }

        // 键名统一大写
        if( $upperCase && is_array( $name ) ) {
            $name = self :: key_uppercase_recursive( $name );
        } else if( $upperCase ) {
            $name = strtoupper( $name );
        }
        // 值的键名大写
        if( $upperCase && is_array( $value ) && count( $value ) ) {
            $value = self :: key_uppercase_recursive( $value );
        }
        // 参数一为数组时，忽略参数二
        if( is_array( $name ) ) {
            return self :: $_cache = array_merge( self :: $_cache, $name );
        }

        $_temp = explode( '.', $name );
        $_key = '';
        // 构造配置多维数组
        for( $i = 0, $n = count( $_temp ); $i < $n; $i ++ ) {
            // 自增数组
            if( $_temp[ $i ] == self :: $_auto ) {
                $_key .= "[]";
            } else {
                $_key .= "['" . $_temp[$i] . "']";
            }
        }
        // 当有运算符并有上次赋值时
        if( array_key_exists( $operator, self :: $operators ) ) {
            $last_value = eval( "return isset( self :: \$_cache$_key ) ? self :: \$_cache$_key : NULL;" );
            if( ! is_null( $last_value ) ) {
                $oper = self :: $operators[ $operator ];
                return eval( "return self :: \$_cache$_key $oper;" );
            }
        }
        return eval( "return self :: \$_cache$_key = \$value;" );

    }

    /**
     * get 方法，获取变量值
     *
     * @static
     * @access public
     * @param String : $name, 变量名称或键名构造
     * @return *
     */
    static public function get( $name = NULL, $upperCase = TRUE ) {
        if( is_null( $name ) ) {
            return self :: $_cache;
        }
        if( $upperCase ) {
            $name = strtoupper( $name );
        }
        $_temp = explode( '.', $name );
        $_key = '';
        for( $i = 0, $n = count( $_temp ); $i < $n; $i ++ ) {
            $_key .= "['" . $_temp[$i] . "']";
        }
        $_key = str_replace( self :: $_dot, '.', $_key );
        return eval( "return isset( self :: \$_cache$_key ) ? self :: \$_cache$_key : NULL;" );
    }

    /**
     * key_uppercase_recursive 方法，递归的对数组中键名大写化
     *
     * @static
     * @access public
     * @param Array : $array
     * @return void
     */
    static public function key_uppercase_recursive( $array = array() ) {

        if( is_array( $array ) && count( $array ) ) {
            $array = array_change_key_case( $array, CASE_UPPER );
            $array = array_map( array( 'Cache', 'key_uppercase_recursive' ), $array );
        }
        return $array;

    }

    /**
     * call 方法，运行一个值做为方法名，此方法不缓存
     *
     * @static
     * @access public
     * @param Object | String : $obj, 对象实例
     * @param String : $name, 变量名称或键名构造
     * @param Mixed : $args, 参数
     * @return *
     */
    static public function call( $name, $args = NULL, & $obj = NULL ) {
        $method = self :: get( $name );
        // Function
        if( is_null( $obj ) || ! is_object( $obj ) ) {
            if( function_exists( $method ) ) {
                if( ! is_null( $args ) ) {
                    if( ! is_array( $args ) ) {
                        $args = array( $args );
                    }
                    return call_user_func_array( $method, $args );
                } else {
                    return $method();
                }
            }
        // Class Method
        } else if( method_exists( $obj, $method ) ) {
            if( ! is_null( $args ) ) {
                if( ! is_array( $args ) ) {
                    $args = array( $args );
                }
                return call_user_func_array( array( $obj, $method ), $args );
            } else {
                return $obj -> $method();
            }
        }
        return NULL;
    }

    /**
     * get_instance 方法，返回一个值做为类名的对象实例, 不缓存对象实例
     *
     * @static
     * @access public
     * @param String : $name
     * @param Array : $args
     * @return Object
     */
    static public function get_instance( $name, $args = NULL ) {
        $class = self :: get( $name );
        if( ! is_null( $name ) && class_exists( $class, FALSE ) ) {
            if( is_null( $args ) ) {
                return new $class;
            } else {
                return new $class( $args );
            }
        }
        return NULL;
    }

    /**
     * load 方法，载入一个配置到缓存，默认项目配置目录
     *
     * @static
     * @access public
     * @param String : $name
     * @param String : $filename
     * @return Boolean
     */
    static public function load( $name, $filename ) {
        static $_load;
        $filename = path_replace( $filename );
        if( ! is_file( $filename ) ) {
            $filename = self :: get( 'APP_PATH.CONFIG' ) . string_append_replace( $filename, '.php' );
        }
        if( isset( $_load[ $filename ] ) ) {
            return TRUE;
        }
        if( is_file( $filename ) ) {
            self :: set( $name, include $filename );
            $_load[ $filename ] = 1;
            return TRUE;
        }
        return FALSE;
    }

    /**
     * save 方法，保存配置
     *
     * @static
     * @access public
     * @param String : $name
     * @param String : $filename
     * @return Boolean
     */
    static public function save( $name, $filename ) {
        if( is_string( $name ) ) {
            $value = self :: get( $name );
        } else {
            $value = $name;
        }
        $value = self :: key_uppercase_recursive( $value );
        $code = '<?PHP' . LINE_BREAK . '/*!' . LINE_BREAK . ' * OpenStation ' . OS_VER . LINE_BREAK . ' *' . LINE_BREAK . ' * @Application : ' . basename( $filename ) . LINE_BREAK . ' * @Description : ';
        if( is_file( $filename ) && preg_match( '/@Description :(.+?)\n/', file_get_contents( $filename ), $tmp ) ) {
            $code .= trim( $tmp[ 1 ] );
        }
        $code .= LINE_BREAK . ' * @Save : ' . date( 'Y-m-d H:i:s' ) . LINE_BREAK . ' */' . LINE_BREAK . LINE_BREAK . 'return ' . var_export( $value, TRUE ) . ';' . LINE_BREAK . LINE_BREAK . '?>';
        return file_put_contents( $filename, $code );
    }

    /**
     * copy 方法，内部复制并再计算赋值
     * 如果尝试把自己复制到自己的子集，将不会有操作并返回NULL
     *
     * @static
     * @access public
     * @param String : $name, 被复制键名构造字符串
     * @param String : $target, 目标键名构造字符串
     * @param String : $value, 对复制的值进行额外赋值运算的值
     * @param String : $operators, 赋值计算
     * @return Null | String
     */
    static public function copy( $name, $target, $value = NULL, $operator = NULL ) {
        $last_value = self :: get( $name );
        if( is_null( $last_value ) || self :: parent( $target ) == strtoupper( $name ) ) {
            return NULL;
        }
        if( ! is_null( $value ) && ! is_null( $operator ) ) {
            self :: set( $target, $last_value );
            $last_value = self :: set( $target, $value, $operator );
        } else {
            self :: set( $target, $last_value );
        }
        return $last_value;
    }

    /**
     * find 方法，尝试用一个值来搜索索引构造字符串
     *
     * @static
     * @access public
     * @param $value 值, 可以是任何类型
     * @param String, $parent 键名构造字符串,指定在这个范围内搜索
     * @param Boolean : Default False, $strict 严格匹配，包括类型
     * @return Null | String
     */
    static public function find( $value, $parent = NULL, $strict = false ) {

        if( is_null( self :: get( $parent ) ) || is_null( $value ) ) {
            return NULL;
        }
        $c = self :: get( $parent );
        if( is_array( $c ) ) {
            foreach( $c as $k => $v ) {
                // 搜索的值是一个数组
                if( is_array( $value ) && $c == $value ) {
                    return $parent;
                }
                $key = $parent ? $parent . '.' . $k : $k;
                $tmp = self :: find( $value, $key, $strict );
                if( is_null( $tmp ) ) {
                    continue;
                }
                return $tmp;
            }
        } else {
            if( $strict == true && $value === $c ) {
                return $parent;
            } else if( $value == $c ) {
                return $parent;
            }
        }
        return NULL;
    }

    /**
     * out 方法，取出(删除)任意位置的一个元素
     *
     * @static
     * @access public
     * @param String : $name, 键名构造字符串
     * @return Null | *
     */
    static public function out( $name = NULL ) {
        $value = self :: get( $name );
        if( ! is_null( $value ) ) {
            if( ! empty( $name ) ) {
                $_temp = explode( '.', strtoupper( $name ) );
                $_key = '';
                for( $i = 0, $n = count( $_temp ); $i < $n; $i ++ ) {
                    $_key .= "['" . $_temp[$i] . "']";
                }
                eval( "unset( self :: \$_cache$_key );" );
            } else {
                self :: $_cache = NULL;
            }
        }
        return $value;
    }

    /**
     * sfhit 方法，取出第一个元素
     *
     * @static
     * @access public
     * @param String : $name, 键名构造字符串
     * @return Null | *
     */
    static public function shift( $name = NULL ) {
        $value = self :: get( $name );
        if( is_null( $value ) ) {
            return $value;
        } else if( is_array( $value ) ) {
            $get = array_shift( $value );
            self :: set( $name, $value );
            return $get;
        }
        return NULL;
    }

    /**
     * pop 方法，取出最后一个元素
     *
     * @static
     * @access public
     * @param String, $name 键名构造字符串
     * @return Null | *
     */
    static public function pop( $name = NULL ) {
        $value = self :: get( $name );
        if( is_null( $value ) ) {
            return $value;
        } else if( is_array( $value ) ) {
            $get = array_pop( $value );
            self :: set( $name, $value );
            return $get;
        }
        return NULL;
    }

    /**
     * append 方法，追加插入(入栈)
     * 非数组会被强制转换为数组，原值作为第一个元素
     *
     * @static
     * @access public
     * @param String : $name, 键名构造字符串
     * @param * : $value, 值
     * @return Null | *
     */
    static public function append( $name = NULL, $value = NULL ) {
        $old = self :: get( $name );
        if( is_null( $old ) || is_array( $old ) ) {
            return self :: set( $name . '.*', $value );
        } else {
            return self :: set( $name, array( $old, $value ) );
        }
    }

    /**
     * prepend 方法，前置插入
     * 非数组会被强制转换为数组，原值作为末尾元素
     *
     * @static
     * @access public
     * @param String : $name, 键名构造字符串
     * @param * : $value, 值
     * @return Null | *
     */
    static public function prepend( $name = NULL, $value = NULL ) {
        $old = self :: get( $name );
        if( is_null( $old ) || is_array( $old ) ) {
            array_unshift( $old, $value );
            return self :: set( $name, $old );
        } else {
            return self :: set( $name, array( $value, $old ) );
        }
    }

    /**
     * parent 方法，获取键名构造的父级构造
     *
     * @static
     * @access public
     * @param String : $name, 键名构造字符串
     * @param Boolean : $root, 获取顶级的父键名
     * @return Null | *
     */
    static public function parent( $name, $root = FALSE ) {
        $_temp = explode( '.', strtoupper(  $name ) );
        if( $root ) {
            $name = $_temp[ 0 ];
        } else {
            array_pop( $_temp );
            $name = implode( '.', $_temp );
        }
        if( $name === '' ) {
            $name = NULL;
        }

        return $name;
    }

    /**
     * type 方法，获取类型或判断类型
     *
     * @static
     * @access public
     * @param String : $name, 键名构造字符串
     * @param String : $is_type, 判断是否此类型，可选值 string | integer | float | boolean | array | object | resource
     * @return Null | String | Boolean
     */
    static public function type( $name, $is_type = NULL ) {

        $value = self :: get( $name );

        $type = NULL;

        switch( is_scalar( $value ) ) {
            case TRUE :
                switch( TRUE ) {
                    case is_string( $value ) : $type = 'string'; break;
                    case is_int( $value ) : $type = 'integer'; break;
                    case is_bool( $value ) : $type = 'boolean'; break;
                    case is_float( $value ) : $type = 'float'; break;
                }
                break;
            // 非标量
            case FALSE :
                switch( TRUE ) {
                    case is_null( $value ) : $type = 'null'; break;
                    case is_array( $value ) : $type = 'array'; break;
                    case is_object( $value ) : $type = 'object'; break;
                    case is_resource( $value ) : $type = 'resource'; break;
                }
                break;
        }

        // 判断类型模式，返回Boolean值
        if( ! is_null( $type ) && ! is_null( $is_type ) ) {
            if( $type == strtolower( $is_type ) ) {
                return TRUE;
            }
            return FALSE;
        }

        return $type;
    }

    /**
     * is_empty 方法, 判断是否为空, 返回布尔值, 注:  0 和 NULL 返回 TRUE
     *
     * @static
     * @access public
     * @param String : $name
     * @return Boolean
     */
    static public function is_empty( $name ) {
        $val = self :: get( $name );
        return empty( $val );
    }

    /**
     * file 方法, 简单文件缓存存取
     *
     * @static
     * @access public
     * @param String : $cfile, 缓存文件路径
     * @param Mixed : $value, 内容
     * @param Boolean : $compressed, 压缩
     * @return Mixed
     */
    static public function file( $cfile, $value = '', $compressed = TRUE ) {

        static $_cache = array();

        if( ! is_file( $cfile ) ) {
            $cfile = self :: get( 'APP_PATH.CACHE' ) . $cfile;
        }
        $cfile = path_replace( $cfile );

        if( $value !== '' ) {
            if( is_null( $value ) ) {
                return rm( $cfile );
            } else {
                $dir = dirname( $cfile );
                mkdirs( $dir );
                $value = json_encode( $value );
                if( $compressed && ! self :: is_empty( 'OPENSTATION.COMPRESSION' ) ) {
                    $value = self :: call( 'OPENSTATION.COMPRESSION.ENCODE', array( $value, 9 ) );
                }
                return file_put_contents( $cfile, $value );
            }

        } else if( is_file( $cfile ) ) {
            $mtime = filemtime( $cfile );
            $unique = unique_string( $mtime . $cfile );
            if( isset( $_cache[ $unique ] ) ) {
                return $_cache[ $unique ];
            }
            $value = file_get_contents( $cfile );
            if( $value{ 0 } != '{' ) {
                $value = self :: call( 'OPENSTATION.COMPRESSION.DECODE', array( $value ) );
            }
            $value = json_decode( $value, TRUE );
            $_cache[ $unique ] = $value;
            return $value;
        }
        return NULL;
    }

    /**
     * lang 方法, 语言包获取及设置
     *
     * @static
     * @access public
     * @param String : $name, 包名称
     * @param Array : $value, 内容
     * @return Mixed
     */
    static public function lang( $name = NULL, $value = array() ) {
        if( empty( $name ) ) {
            return self :: get( self :: LANG_NS );
        }

        if( is_array( $name ) ) {
            return self :: set( self :: LANG_NS, $name ) ;
        } else if( ! empty( $value ) ) {
            return self :: set( self :: LANG_NS . '.' . $name, $value );
        }
        return self :: get( self :: LANG_NS . '.' . $name );
    }
}
?>